package com.hqd.jjvm.jnative.lang;

import com.hqd.jjvm.jnative.JNativeRegistry;
import com.hqd.jjvm.rtda.JThread;
import com.hqd.jjvm.rtda.StackFrame;
import com.hqd.jjvm.rtda.heap.JClass;
import com.hqd.jjvm.rtda.heap.JMethod;
import com.hqd.jjvm.rtda.obj.JObject;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

public class JThrowableNativeRegistry extends JNativeRegistry {

    private static final JThrowableNativeRegistry instance = new JThrowableNativeRegistry();

    static {
        registry("java/lang/Throwable", "fillInStackTrace", "(I)Ljava/lang/Throwable;", JThrowableNativeRegistry::fillInStackTrace);
    }

    protected static void fillInStackTrace(JThread jThread) {
        JObject ref = jThread.getJvmStack().getTop().getLocalVars().getRefVal(0);
        jThread.getJvmStack().pushOperandStackRefVal(ref);
        ref.setExtra(createStackTraceElements(ref, jThread));
    }

    protected static JStackTraceElement[] createStackTraceElements(JObject exJObject, JThread jThread) {
        int skip = distanceToObject(exJObject.getJClass()) + 2;
        StackFrame[] sfs = new StackFrame[jThread.getJvmStack().size() - skip];
        JStackTraceElement[] jstes = new JStackTraceElement[sfs.length];
        for (int i = 0; i < sfs.length; i++) {
            StackFrame sf = jThread.getJvmStack().elementAt(sfs.length - i - 1);
            JClass jc = sf.getJClass();
            JMethod jm = sf.getJMethod();
            JStackTraceElement ste = new JStackTraceElement(jc.getSourceFile(), jc.getClassName(), jm.getName(), jm.getLineNumber(sf.getReturnAddress().get() - 1));
            jstes[i] = ste;
        }
        return jstes;
    }

    private static int distanceToObject(JClass exJClass) {
        int distance = 0;
        for (JClass jc = exJClass.getSuperClass(); jc != null; jc = jc.getSuperClass()) {
            distance++;
        }
        return distance;
    }

    public static JThrowableNativeRegistry getInstance() {
        return instance;
    }

    /**
     * 虚拟机栈信息
     */
    @Getter
    @Setter
    @NoArgsConstructor
    @AllArgsConstructor
    static
    class JStackTraceElement {
        private String fileName;
        private String className;
        private String methodName;
        private Integer lineNumber;
    }
}
